package org.fhm.zdte.common.task;


import org.fhm.substrate.annotation.Component;
import org.fhm.substrate.annotation.Setup;
import org.fhm.substrate.service.LoggerHandler;
import org.fhm.substrate.standard.ILogger;
import org.fhm.zdte.common.ability.IGetIdentification;
import org.fhm.zdte.common.ability.IIsOffLineByIdentification;
import org.fhm.zdte.common.ability.IRefreshBaseTime;
import org.fhm.zdte.common.constant.protocol.PacketType;
import org.fhm.zdte.common.constant.protocol.ResponseMessageType;
import org.fhm.zdte.common.pojo.Address;
import org.fhm.zdte.common.pool.ThreadPoolFactory;
import org.fhm.zdte.common.protocol.AbstractMessageStandard;
import org.fhm.zdte.common.standard.IChannelDispatcher;

import java.util.Objects;

/**
 * Packet distribute task.
 *
 * @author 谭波
 * @since 2023/11/4
 */
@Component
public class ReceiveMessageDistributorTask extends AbstractDistributorTask {

    private final ILogger logger = LoggerHandler.getLogger(ReceiveMessageDistributorTask.class);

    protected byte[] buffer;

    private ReceiveMessageDistributorTask owner;

    @Setup
    private IIsOffLineByIdentification isOffLineByIdentification;

    @Setup
    private IChannelDispatcher iChannelDispatcher;

    @Setup
    private IRefreshBaseTime refreshBaseTime;

    @Setup
    private IGetIdentification iGetIdentification;

    @SuppressWarnings("unused")
    public ReceiveMessageDistributorTask() {
    }

    public ReceiveMessageDistributorTask(
            ReceiveMessageDistributorTask receiveMessageDistributorTask,
            byte[] buffer, Address address
    ) {
        this.owner = receiveMessageDistributorTask;
        this.buffer = buffer;
        this.address = address;
    }

    @Override
    public void run() {
        try {
            AbstractMessageStandard messageStandard;
            if (owner.abstractMessageMonitor
                    .isLawful(
                            (messageStandard = owner.abstractMessageParser.decoding(buffer))
                    )
            ) {
                if (PacketType.ONLINE.getName().equals(
                        PacketType.getPacketNameBy(
                                messageStandard.getPacketType()
                        )
                )) {
                    owner.receiveDealTask.getTaskAndSubmit(messageStandard, address);
                } else {
                    String from = messageStandard.getFrom();
                    owner.refreshBaseTime.refreshBaseTime(from);
                    if (
                            Objects.nonNull(owner.isOffLineByIdentification.isOffLineByIdentification(null))
                                    && owner.isOffLineByIdentification.isOffLineByIdentification(from)
                    ) {
                        owner.iChannelDispatcher.obtainChannelManager(address)
                                .sendMessage(owner.abstractMessageWrappers.getFailAckMessageByPacketType(
                                        PacketType.ONLINE, ResponseMessageType.USER_IS_OFF, null,
                                        owner.iGetIdentification.getIdentification(), "", null,
                                        from), address);
                        return;
                    }
                    if (messageStandard.getPacketType().equals(PacketType.REQUEST) && Objects.isNull(messageStandard.getContentType())) {
                        logger.warn("accept the error request message");
                        return;
                    }
                    owner.receiveDealTask.getTaskAndSubmit(messageStandard, address);
                }
            }
        } catch (Exception e) {
            logger.error(e, "failed to distribute message");
        }
    }

    @Override
    public void getTaskAndSubmit(byte[] buffer, Address address) {
        try {
            threadPoolFactory
                    .submitTask(
                            ThreadPoolFactory.DISTRIBUTOR_THREAD_POOL,
                            new ReceiveMessageDistributorTask(this, buffer, address)
                    );
        } catch (Exception e) {
            logger.warn("task submit fail {} {}", e.getMessage(), e.getClass());
        }
    }
}
